package com.itmake.sample.springfox.config;

import com.itmake.sample.springfox.property.SwaggerProperty;
import com.itmake.sample.springfox.constant.SwaggerConstant;
import io.swagger.models.auth.In;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.List;

/**
 * Swagger 配置
 *
 * @author Amjacks
 * @since 2023/08/10
 */
@Configuration
@EnableOpenApi
@EnableAutoConfiguration
public class SwaggerConfig {

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperty swaggerProperties() {
        return new SwaggerProperty();
    }

    /**
     * 创建 API
     */
    @Bean
    public Docket api(SwaggerProperty swaggerProperty) {
        // base-path 处理
        if (swaggerProperty.getBasePath().isEmpty()) {
            swaggerProperty.getBasePath().add(SwaggerConstant.BASE_PATH);
        }
        // exclude-path 处理
        if (swaggerProperty.getExcludePath().isEmpty()) {
            swaggerProperty.getExcludePath().addAll(SwaggerConstant.DEFAULT_EXCLUDE_PATH);
        }
        // 构建
        ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30)
                // 是否启用
                .enable(swaggerProperty.getEnabled())
                .host(swaggerProperty.getHost())
                // 用来创建该 API 的基本信息，展示在文档的页面中（自定义展示的信息）
                .apiInfo(apiInfo(swaggerProperty)).select()
                // 扫描指定包中的 swagger 注解
                .apis(RequestHandlerSelectors.basePackage(swaggerProperty.getBasePackage()));
        swaggerProperty.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
        swaggerProperty.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
        // 设置安全模式，swagger 可以设置访问 token
        return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/");
    }

    /**
     * 安全模式，这里指定 token 通过 Authorization 头请求头传递
     */
    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> apiKeyList = new ArrayList<>();
        apiKeyList.add(new ApiKey(SwaggerConstant.AUTHORIZATION, SwaggerConstant.AUTHORIZATION, In.HEADER.toValue()));
        return apiKeyList;
    }

    /**
     * 安全上下文
     */
    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
                        .build());
        return securityContexts;
    }

    /**
     * 默认的全局鉴权策略
     */
    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference(SwaggerConstant.AUTHORIZATION, authorizationScopes));
        return securityReferences;
    }

    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo(SwaggerProperty swaggerProperty) {
        // 用 ApiInfoBuilder 进行定制
        return new ApiInfoBuilder()
                .title(swaggerProperty.getTitle())
                .description(swaggerProperty.getDescription())
                .license(swaggerProperty.getLicense())
                .licenseUrl(swaggerProperty.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperty.getTermsOfServiceUrl())
                .contact(new Contact(swaggerProperty.getContact().getName(), swaggerProperty.getContact().getUrl(), swaggerProperty.getContact().getEmail()))
                .version(swaggerProperty.getVersion())
                .build();
    }
}
